package com.fishV.BI.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fishV.BI.MQ.RabbitMQProducer;
import com.fishV.BI.common.ErrorCode;
import com.fishV.BI.constant.CommonConstant;
import com.fishV.BI.constant.UserConstant;
import com.fishV.BI.exception.BusinessException;
import com.fishV.BI.exception.ThrowUtils;
import com.fishV.BI.manager.RedisLimiterManager;
import com.fishV.BI.mapper.ChartMapper;
import com.fishV.BI.model.dto.chart.ChartQueryRequest;
import com.fishV.BI.model.dto.chart.GenChartByAiRequest;
import com.fishV.BI.model.entity.*;
import com.fishV.BI.model.vo.EchartResponseVO;
import com.fishV.BI.model.vo.UserVO;
import com.fishV.BI.service.ChartService;
import com.fishV.BI.utils.AIUtils;
import com.fishV.BI.utils.ExcelUtils;
import com.fishV.BI.utils.SqlUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RateIntervalUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Date;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

/**
 *
 */
@Service
public class ChartServiceImpl extends ServiceImpl<ChartMapper, Chart> implements IService<Chart>, ChartService {

    @Resource
    AIUtils aiUtils;
    @Resource
    private RedisLimiterManager redisLimiterManager;
    @Autowired
    private ThreadPoolExecutor executor;
    @Autowired
    private RabbitMQProducer producer;


    static final Long MODE_ID = 1847239670998675458L;

    @Override
    public QueryWrapper<Chart> getQueryWrapper(ChartQueryRequest chartQueryRequest) {
        QueryWrapper<Chart> queryWrapper = new QueryWrapper<>();

        if (chartQueryRequest == null) {
            return queryWrapper;
        }
        String sortField = chartQueryRequest.getSortField();
        String sortOrder = chartQueryRequest.getSortOrder();
        Long id = chartQueryRequest.getId();
        Long userId = chartQueryRequest.getUserId();

        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC), sortField);
        return queryWrapper;
    }

    @Override
    public EchartResponseVO genEchart(MultipartFile multipartFile, GenChartByAiRequest genEchartRequest, HttpServletRequest request) {

        //得到用户信息
        HttpSession session = request.getSession();
        User user = (User) session.getAttribute(UserConstant.USER_LOGIN_STATE);
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
        redisLimiterManager.rateLimit(user.getId() + "genEchart", 1, 1);

        String name = genEchartRequest.getName();
        String goal = genEchartRequest.getGoal();
        String type = genEchartRequest.getChartType();
        if (StringUtils.isEmpty(goal)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "目标不能为空");
        }
        if (name == null || name.length() > 10) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "图表名称不合法");
        }
        ThrowUtils.throwIf(StringUtils.isEmpty(type), ErrorCode.PARAMS_ERROR, "图表类型不能为空");
        //压缩后的数据
        String data = ExcelUtils.readExcel(multipartFile);
        StringBuilder userInput = new StringBuilder();
        userInput.append("分析目标：");
        if (StringUtils.isNotEmpty(type)) {
            goal = goal + "，图表类型为：" + type;
        }
        userInput.append(goal);
        userInput.append("\n");
        userInput.append("数据：");
        userInput.append(data);
        String response = aiUtils.getAiResult(true, userInput.toString());
        String[] result = response.split("result:");
        ThrowUtils.throwIf(result.length < 2, ErrorCode.SYSTEM_ERROR, "AI分析失败");

        //存入数据库
        Chart chart = new Chart();
        chart.setName(name);
        chart.setGoal(goal);
        chart.setChartData(data);
        chart.setChartType(type);
        chart.setGenChart(result[1].trim());
        chart.setGenResult(result[2].trim());
        chart.setStatus("1");
        chart.setExecMessage("成功");

        chart.setUserId(user.getId());
        chart.setCreateTime(new Date());
        chart.setUpdateTime(new Date());
        save(chart);
        EchartResponseVO echartResponseVO = new EchartResponseVO();
        echartResponseVO.setResponse(result[2].trim());
        echartResponseVO.setOptions(result[1].trim());
        return echartResponseVO;
    }


    @Override
    public EchartResponseVO genEchartAsync(MultipartFile multipartFile, GenChartByAiRequest genEchartRequest, HttpServletRequest request) {

        //得到用户信息
        HttpSession session = request.getSession();
        User user = (User) session.getAttribute(UserConstant.USER_LOGIN_STATE);
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
        redisLimiterManager.rateLimit(user.getId() + "genEchart", 1, 1);

        String name = genEchartRequest.getName();
        String goal = genEchartRequest.getGoal();
        String type = genEchartRequest.getChartType();
        if (StringUtils.isEmpty(goal)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "目标不能为空");
        }
        if (name == null || name.length() > 10) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "图表名称不合法");
        }
        ThrowUtils.throwIf(StringUtils.isEmpty(type), ErrorCode.PARAMS_ERROR, "图表类型不能为空");
        //压缩后的数据
        String data = ExcelUtils.readExcel(multipartFile);
        StringBuilder userInput = new StringBuilder();
        userInput.append("分析目标：");
        if (StringUtils.isNotEmpty(type)) {
            goal = goal + "，图表类型为：" + type;
        }
        userInput.append(goal);
        userInput.append("\n");
        userInput.append("数据：");
        userInput.append(data);
        //存入数据库
        Chart chart = new Chart();
        chart.setName(name);
        chart.setGoal(goal);
        chart.setChartData(data);
        chart.setChartType(type);
        chart.setStatus("waiting");
        chart.setExecMessage("等待中");
        chart.setUserId(user.getId());
        chart.setCreateTime(new Date());
        chart.setUpdateTime(new Date());
        Boolean flag = save(chart);
        Long chartId = chart.getId();
        if (!flag) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "系统内部错误");
        }


        CompletableFuture.runAsync(() -> {
            Chart chart0 = new Chart();
            chart0.setId(chartId);
            chart0.setStatus("running");
            chart0.setExecMessage("正在分析中，请稍后");
            Boolean res1 = updateById(chart0);
            if(!res1){
                handleException(chartId,"启动失败");
                return;
            }

            String response = aiUtils.getAiResult(true, userInput.toString());
            String[] result = response.split("result:");
            if(result.length < 3){
                handleException(chartId,"AI分析失败");
                return ;
            }
            Chart chart1 = new Chart();
            chart1.setId(chartId);
            chart1.setGenChart(result[1].trim());
            chart1.setStatus("success");
            chart1.setExecMessage("分析成功");
            chart1.setGenResult(result[2].trim());
            Boolean res2 = updateById(chart1);
            if(!res2){
                handleException(chartId,"AI分析失败");
                return ;
            }

        }, executor);


        EchartResponseVO echartResponseVO = new EchartResponseVO();
        return echartResponseVO;
    }




    @Override
    public EchartResponseVO genEchartAsyncMq(MultipartFile multipartFile, GenChartByAiRequest genEchartRequest, HttpServletRequest request) {

        //得到用户信息
        HttpSession session = request.getSession();
        User user = (User) session.getAttribute(UserConstant.USER_LOGIN_STATE);
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
        redisLimiterManager.rateLimit(user.getId() + "genEchart", 1, 1);

        String name = genEchartRequest.getName();
        String goal = genEchartRequest.getGoal();
        String type = genEchartRequest.getChartType();
        if (StringUtils.isEmpty(goal)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "目标不能为空");
        }
        if (name == null || name.length() > 10) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "图表名称不合法");
        }
        ThrowUtils.throwIf(StringUtils.isEmpty(type), ErrorCode.PARAMS_ERROR, "图表类型不能为空");
        //压缩后的数据
        String data = ExcelUtils.readExcel(multipartFile);
        StringBuilder userInput = new StringBuilder();
        userInput.append("分析目标：");
        if (StringUtils.isNotEmpty(type)) {
            goal = goal + "，图表类型为：" + type;
        }
        userInput.append(goal);
        userInput.append("\n");
        userInput.append("数据：");
        userInput.append(data);
        //存入数据库
        Chart chart = new Chart();
        chart.setName(name);
        chart.setGoal(goal);
        chart.setChartData(data);
        chart.setChartType(type);
        chart.setStatus("waiting");
        chart.setExecMessage("等待中");
        chart.setUserId(user.getId());
        chart.setCreateTime(new Date());
        chart.setUpdateTime(new Date());
        Boolean flag = save(chart);
        Long chartId = chart.getId();
        if (!flag) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "系统内部错误");
        }
        producer.sendMessage(chart);
        EchartResponseVO echartResponseVO = new EchartResponseVO();
        return echartResponseVO;
    }
    public void handleException(Long chartId,String message){
        Chart chart = new Chart();
        chart.setId(chartId);
        chart.setStatus("failed");
        chart.setExecMessage(message);
        updateById(chart);
    }

}





